package pro.seac.twitter.application;

import static org.apache.commons.collections.CollectionUtils.isEmpty;

import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import pro.seac.twitter.application.dao.UserAccountDao;
import pro.seac.twitter.application.dto.UserAccountData;
import pro.seac.twitter.application.dto.UserAccountDataAssembler;
import pro.seac.twitter.application.dto.UserViewEntry;
import pro.seac.twitter.domain.UserAccount;

@Service
public class UserManagementService {
	public static final String USER_NAME_PATTERN = "[a-z0-9_-]{3,15}";
	@Autowired
	private UserAccountDao userAccountDao;
	private UserAccountDataAssembler assembler;

	public UserManagementService() {
		this.assembler = new UserAccountDataAssembler();
	}

	/* *** commands * */

	@Transactional
	public Integer registerUserAccount(UserAccountData data) {
		String name = data.getUsername();
		if (userAccountDao.findByUserName(name) != null) { throw new IllegalArgumentException("User with name: " + name + " already exists"); }
		UserAccount account = assembler.mapToModel(data);
		account.beforePersist(); // TODO przeniesc do listenera
		userAccountDao.save(account);
		return account.getId();
	}

	@Transactional
	@Secured("ROLE_USER")
	public void updateUserAccount(UserAccountData data) {
		UserAccount account = assembler.mapToModel(data);
		account.beforeUpdate(); // TODO przeniesc do listenera
		userAccountDao.save(account);
	}

	@Transactional
	public void startFollowingUser(Integer userId, Integer followingUserId) {
		if (userId == followingUserId) { throw new IllegalArgumentException("followingUserId"); }
		UserAccount user = userAccountDao.findOne(userId);
		if (user.isAlreadyFollowingUser(followingUserId)) { throw new IllegalArgumentException("followingUserId"); }
		UserAccount following = userAccountDao.findOne(followingUserId);
		user.addFollowingUser(following);
		user.beforeUpdate(); // TODO przeniesc do listenera
		userAccountDao.save(user);
	}

	public void stopFollowingUser(Integer userId, Integer followingUserId) {
		UserAccount user = userAccountDao.findOne(userId);
		UserAccount following = userAccountDao.findOne(followingUserId);
		user.removeFollowingUser(following);
		user.beforeUpdate(); // TODO przeniesc do listenera
		userAccountDao.save(user);
	}

	/* *** queries * */

	@Transactional(readOnly = true)
	public UserAccountData getUserAccountById(Integer id) {
		UserAccount account = userAccountDao.findOne(id);
		return account != null ? assembler.mapToDto(account) : null;
	}

	@Transactional(readOnly = true)
	public UserAccountData getUserAccountByUserName(String username) throws UsernameNotFoundException {
		UserAccount account = userAccountDao.findByUserName(username);
		if (account == null) { throw new UsernameNotFoundException(username); }
		return assembler.mapToDto(account);
	}

	@Transactional(readOnly = true)
	public UserViewEntry getUserProfileData(String username) {
		return userAccountDao.getByUserName(username);
	}

	@Transactional(readOnly = true)
	public List<UserViewEntry> getAllUserProfiles() {
		List<UserViewEntry> list = userAccountDao.getAll();
		return isEmpty(list) ? Collections.<UserViewEntry> emptyList() : list;
	}

	@Transactional(readOnly = true)
	/** Pobiera liste osob sledzacych uzytkownika */
	public List<UserViewEntry> getFollowers(Integer id) {
		List<UserViewEntry> list = userAccountDao.getFollowersById(id);
		return isEmpty(list) ? Collections.<UserViewEntry> emptyList() : list;
	}

	@Transactional(readOnly = true)
	/** Pobiera liste osob sledzonych przez uzytkownika */
	public List<UserViewEntry> getFollowed(Integer id) {
		List<UserViewEntry> list = userAccountDao.getFollowedById(id);
		return isEmpty(list) ? Collections.<UserViewEntry> emptyList() : list;
	}

}
